package org.elfen.common.scan;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;

import java.io.IOException;
import java.lang.reflect.Method;

import javax.servlet.http.HttpServletRequest;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.reflect.MethodSignature;
import org.elfen.common.Constants;
import org.elfen.common.entity.SysLog;
import org.elfen.common.util.DataUtil;
import org.elfen.common.util.DateUtil;
import org.elfen.common.util.WebUtil;
import org.elfen.redis.service.RedisService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import com.alibaba.fastjson.JSON;

import cz.mallat.uasparser.OnlineUpdater;
import cz.mallat.uasparser.UASparser;
import cz.mallat.uasparser.UserAgentInfo;

/**
 * 系统日志拦截器的基础抽象
 * 
 * @author elfen
 */
@Component
public class BaseSysLogAspect4Around {
	protected static Logger logger = LoggerFactory.getLogger(BaseSysLogAspect4Around.class);
	@Autowired
	private RedisService redisService;
	private static String topic;
	
	static UASparser uasParser = null;
	static {// 初始化uasParser对象
		topic = Constants.SYS_LOG_TOPIC;
		try {
			uasParser = new UASparser(OnlineUpdater.getVendoredInputStream());
		} catch (IOException e) {
			logger.error("", e);
		}
	}

	/**
     *  前置通知（Before advice）：
     *  	在某连接点（JoinPoint）之前执行的通知，但这个通知不能阻止连接点前的执行。
     *  	ApplicationContext中在<aop:aspect>里面使用<aop:before>元素进行声明。
     *  	例如，TestAspect中的doBefore方法。
		后置通知（After advice）：
			当某连接点退出的时候执行的通知（不论是正常返回还是异常退出）。
			ApplicationContext中在<aop:aspect>里面使用<aop:after>元素进行声明。
			例如，ServiceAspect中的returnAfter方法，所以Teser中调用UserService.delete抛出异常时，returnAfter方法仍然执行。
		返回后通知（After return advice）：
			在某连接点正常完成后执行的通知，不包括抛出异常的情况。
			ApplicationContext中在<aop:aspect>里面使用<after-returning>元素进行声明。
		环绕通知（Around advice）：包围一个连接点的通知，类似Web中Servlet规范中的Filter的doFilter方法。
			可以在方法的调用前后完成自定义的行为，也可以选择不执行。
			ApplicationContext中在<aop:aspect>里面使用<aop:around>元素进行声明。
			例如，ServiceAspect中的around方法。
		抛出异常后通知（After throwing advice）：在方法抛出异常退出时执行的通知。
			ApplicationContext中在<aop:aspect>里面使用<aop:after-throwing>元素进行声明。
			例如，ServiceAspect中的returnThrow方法。
	 * @throws Throwable 
     */
	public Object aroundLog(ProceedingJoinPoint joinPoint) throws Throwable {
		long beginTime = System.currentTimeMillis();
		// 接收到请求，记录请求内容
		ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder
				.getRequestAttributes();
		HttpServletRequest request = attributes.getRequest();
		String uri = request.getRequestURI();
		// 获取客户端信息
		String userAgent = "";
		try {
			UserAgentInfo userAgentInfo = uasParser.parse(request.getHeader("user-agent"));
			userAgent = userAgentInfo.getOsName() + " " + userAgentInfo.getType() + " " + userAgentInfo.getUaName();
		} catch (IOException e) {
			logger.error(" 获取客户端信息，出现异常！"+e.getLocalizedMessage());
		}
		
		String title = "";
		Signature sign = joinPoint.getSignature();
		try {
			Class clzz = sign.getDeclaringType();
			Api api = (Api) clzz.getAnnotation(Api.class);
			
			MethodSignature msign = (MethodSignature)sign;
			Method targetMethod = msign.getMethod();
			ApiOperation apiOperation = (ApiOperation) targetMethod.getAnnotation(ApiOperation.class);
			title = api.value()+"："+apiOperation.value();
		} catch (Exception e2) {
			logger.info("获取方法的ApiOperation注解信息，出现异常："+e2.getLocalizedMessage());
		}
		
		String beanName = sign.getDeclaringTypeName();
		String methodName = sign.getName();
		Long userId = -1L;
		if(DataUtil.isNotEmpty(WebUtil.getCurrentUser(request))){
			try {
				userId = Long.parseLong((String)WebUtil.getCurrentUser(request));
			} catch (Exception e1) {
				logger.info("UserId转化为long类型出现异常："+e1.getLocalizedMessage());
			}
		}
		String method = request.getMethod();
		String params = "";
		Object[] paramsArray = joinPoint.getArgs();
		if(DataUtil.isNotEmpty(paramsArray)){
			params = argsArrayToString(paramsArray);
		}
//		Map<?, ?> paramsMap = (Map<?, ?>) request.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
//		if(DataUtil.isNotEmpty(paramsMap)){
//			params += paramsMap.toString();
//		}
		
		logger.info("title="+title+"; uri=" + uri + "; beanName=" + beanName + ";  userId=" + userId 
					+ "; methodName=" + methodName + "; params=" + params);

		final SysLog record = new SysLog();
		record.setTitle(title);
		record.setMethodName(methodName);
		record.setMethod(method);
		record.setRequestUri(uri);
		record.setClientHost(WebUtil.getHost(request));
		record.setUserAgent(userAgent);
		record.setParameters(params != null ? params.toString() : "");
		record.setCreateBy(userId);
		record.setCreateTime(DateUtil.getNow());
		record.setRequestTime(beginTime);
		
		//返回结果
		Object result = null;
		try {
			result = joinPoint.proceed();//执行业务逻辑方法，得到响应结果
			
			record.setRemark(JSON.toJSON(result).toString());
			long requestTime = System.currentTimeMillis() - beginTime;
			record.setRequestTime(requestTime);
			
			if (!uri.contains("/read/") && !uri.contains("/unauthorized") 
					&& !uri.contains("/forbidden") && !uri.contains("/swagger-resources")) {
				//保存DB
				//activeMQProducerService.sendMessage(QueueNameConstants.LOG_QUEUE, JSON.toJSONString(record));
				redisService.sendMessage2Topic(topic,JSON.toJSONString(record));
				
			} else {
				//非关键操作的正常日志，就不存入DB
			}
			
		} catch (Throwable e) {
			record.setRemark("出现异常："+e.getMessage());
			long requestTime = System.currentTimeMillis() - beginTime;
			record.setRequestTime(requestTime);
			//所有访问的异常日志，都存入DB
			//activeMQProducerService.sendMessage(QueueNameConstants.LOG_QUEUE, JSON.toJSONString(record));
			redisService.sendMessage2Topic(topic,JSON.toJSONString(record));
			
			//抛出异常，从而方便Controller层统一进行异常处理
			throw e; 
		}
		
		return result;
			
	}

	/**
	 * 请求参数拼装
	 * 
	 * @param paramsArray
	 * @return
	 */
	private String argsArrayToString(Object[] paramsArray) {
		String params = "";
		try {
			if (paramsArray != null && paramsArray.length > 0) {
				for (int i = 0; i < paramsArray.length; i++) {
					Object paramsObj = paramsArray[i];
					if(DataUtil.isNotEmpty(paramsObj)){
						if(paramsObj instanceof org.springframework.validation.BindingResult){
							continue;
						}
						logger.debug("获取到的参数paramsObj：{}",paramsObj);
						Object jsonObj = JSON.toJSON(paramsObj);
						params += jsonObj.toString() + " ";
					}
				}
			}
		} catch (Exception e) {
			logger.error("获取POST请求的参数，并转换为JSON时出现异常："+e.getLocalizedMessage());
		}
		return params.trim();
	}
}
